﻿@using AntDesign.Internal
@using AntDesign.Core.JsInterop.Modules.Components
@using System.Threading;
@using static AntDesign.TextArea;
@inherits AntDesignTestBase	

@code {
    private void SetupJavascript()
    {
        JSInterop.SetupVoid("AntDesign.interop.mentionsHelper.setEditorKeyHandler", _ => true);
        JSInterop.Setup<int>("AntDesign.interop.mentionsHelper.getProp", _ => true).SetResult(1);
        JSInterop.Setup<Double[]>("AntDesign.interop.mentionsHelper.setPopShowFlag", _ => true).SetResult(new[] { 1d, 1d });
        JSInterop.Setup<Double[]>("AntDesign.interop.mentionsHelper.getCursorXY", _ => true).SetResult(new[] { 1d, 1d });
    }


    private IRenderedComponent<AntDesign.Mentions> GetStandardRenderSystemUnderTest()
    {
        return Render<AntDesign.Mentions>(
            @<AntDesign.Mentions>
                <AntDesign.MentionsOption Value="testUsername">Test User</AntDesign.MentionsOption>
                <AntDesign.MentionsOption Value="anotherUsername">Another User</AntDesign.MentionsOption>
            </AntDesign.Mentions>
        );
    }

    private IRenderedComponent<AntDesign.Mentions> GetLoadOptionsRenderSystemUnderTest(Func<string, CancellationToken, Task<IEnumerable<MentionsDynamicOption>>> loadOptions)
    {
        return Render<AntDesign.Mentions>(
            @<AntDesign.Mentions LoadOptions="@loadOptions" />
        );
    }

    [Theory]
    [InlineData("")]
    [InlineData("username")]
    public async Task ItShouldCallLoadOptionsWithSearchValueWhenUserEntersValueAfterPrefix(string testEntry)
    {
        SetupJavascript();
        JSInterop.Setup<int>(
            "AntDesign.interop.mentionsHelper.getProp", 
            invocation => (string?)invocation.Arguments[1] == "selectionStart")
            .SetResult(testEntry.Length + 1);

        string? valueSentToCallback = null;
        var systemUnderTest = GetLoadOptionsRenderSystemUnderTest((value, _) =>
        {
            valueSentToCallback = value;

            return Task.FromResult(Enumerable.Empty<MentionsDynamicOption>());
        });

        var textArea = systemUnderTest.Find("textarea");
        
        await textArea.InputAsync(new ChangeEventArgs
        {
            Value = "@" + testEntry
        });

        valueSentToCallback.Should().BeEquivalentTo(testEntry);
    }

    [Fact]
    public async Task ItShouldDisplayOptionsReturnedFromLoadOptionsCall()
    {
        var testEntry = "username";

        SetupJavascript();
        JSInterop.Setup<int>(
            "AntDesign.interop.mentionsHelper.getProp", 
            invocation => (string?)invocation.Arguments[1] == "selectionStart")
            .SetResult(testEntry.Length + 1);

        var systemUnderTest = GetLoadOptionsRenderSystemUnderTest((value, _) =>
        {
            return Task.FromResult(new[]
            {
                new MentionsDynamicOption
                {
                    Display = @<span>User Name 1</span>,
                    Value = "username1"
                },
                new MentionsDynamicOption
                {
                    Display = @<span>User Name 2</span>,
                    Value = "username2"
                }
            }.AsEnumerable());
        });

        var textArea = systemUnderTest.Find("textarea");
        
        await textArea.InputAsync(new ChangeEventArgs
        {
            Value = "@" + testEntry
        });

        systemUnderTest.MarkupMatches(@"
            <div class=""ant-mentions"">
                <textarea value="""" class=""rc-textarea"" rows=""3""></textarea>
            </div>
            <div class:ignore style:ignore>
                <ul class:ignore>
                    <li class=""ant-mentions-dropdown-menu-item ant-mentions-dropdown-menu-item-active"">
                        <span>User Name 1</span>
                    </li>
                    <li class=""ant-mentions-dropdown-menu-item"">
                        <span>User Name 2</span>
                    </li>
                </ul>
            </div>");
    }

    [Fact]
    public async Task ItShouldDisplayOptionsWithValueContainingSearchValueWhenUserTypesForStaticOptions()
    {
        var testEntry = "anoth";

        SetupJavascript();
        JSInterop.Setup<int>(
            "AntDesign.interop.mentionsHelper.getProp", 
            invocation => (string?)invocation.Arguments[1] == "selectionStart")
            .SetResult(testEntry.Length + 1);

        var systemUnderTest = GetStandardRenderSystemUnderTest();

        var textArea = systemUnderTest.Find("textarea");
        
        await textArea.InputAsync(new ChangeEventArgs
        {
            Value = "@" + testEntry
        });

        systemUnderTest.MarkupMatches(@"
            <div class=""ant-mentions"">
                <textarea value="""" class=""rc-textarea"" rows=""3""></textarea>
            </div>
            <div class:ignore style:ignore>
                <ul class:ignore>
                    <li class=""ant-mentions-dropdown-menu-item ant-mentions-dropdown-menu-item-active"">
                        Another User
                    </li>
                </ul>
            </div>");
    }

    [Fact]
    public void ItShouldRenderByDefaultWithTextarea()
    {
        SetupJavascript();
        var systemUnderTest = GetStandardRenderSystemUnderTest();

        systemUnderTest.MarkupMatches(@"
            <div class=""ant-mentions"">
                <textarea value="""" class=""rc-textarea"" rows=""3""></textarea>
            </div>
            <div class=""ant-mentions-dropdown ant-mentions-dropdown-placement-bottomLeft ant-mentions-dropdown-hidden""
                style=""position: absolute; display: none;"">
                <ul class=""ant-mentions-dropdown-menu ant-mentions-dropdown-menu-root ant-mentions-dropdown-menu-vertical"">
                    <li class=""ant-mentions-dropdown-menu-item"">
                        Test User
                    </li>
                    <li class=""ant-mentions-dropdown-menu-item"">
                        Another User
                    </li>
                </ul>
            </div>");
    }

    [Fact]
    public void StandardRender_ShouldCallJavascriptCallbackToDisplayOverlayWhenTypingPrefix()
    {
        SetupJavascript();
        var systemUnderTest = GetStandardRenderSystemUnderTest();

        JSInterop.Setup<Double[]>("AntDesign.interop.mentionsHelper.setPopShowFlag", _ => true);

        systemUnderTest.Find("textarea").Input("@");

        JSInterop.VerifyInvoke("AntDesign.interop.mentionsHelper.setPopShowFlag", 1);
    }

    [Fact]
    public void ShouldCallJavascriptCallbackToDisplayOverlayWhenTypingProvidedPrefix()
    {
        const string prefix = "#";

        SetupJavascript();
        var systemUnderTest = Render<AntDesign.Mentions>(
            @<AntDesign.Mentions Prefix="@prefix">
                <AntDesign.MentionsOption Value="testUsername">Test User</AntDesign.MentionsOption>
                <AntDesign.MentionsOption Value="anotherUsername">Another User</AntDesign.MentionsOption>
            </AntDesign.Mentions>
        );

        JSInterop.Setup<Double[]>("AntDesign.interop.mentionsHelper.setPopShowFlag", _ => true);

        systemUnderTest.Find("textarea").Input(prefix);

        JSInterop.VerifyInvoke("AntDesign.interop.mentionsHelper.setPopShowFlag", 1);
    }

    [Fact]
    public async Task ItShouldDisplayAllOptionsWhenUserTypesOnlyPrefix()
    {
        SetupJavascript();
        JSInterop.Setup<int>(
            "AntDesign.interop.mentionsHelper.getProp", 
            invocation => (string?)invocation.Arguments[1] == "selectionStart")
            .SetResult(1);

        var systemUnderTest = GetStandardRenderSystemUnderTest();

        var textArea = systemUnderTest.Find("textarea");
        
        await textArea.InputAsync(new ChangeEventArgs
        {
            Value = "@"
        });

        // 验证显示了所有选项(未筛选)
        var options = systemUnderTest.FindAll(".ant-mentions-dropdown-menu-item");
        options.Count.Should().Be(2);
        
        // 验证第一个选项是激活状态
        systemUnderTest.FindAll(".ant-mentions-dropdown-menu-item-active").Count.Should().Be(1);
    }

    [Fact]
    public void ItShouldSupportMultiplePrefixes()
    {
        const string prefixes = "@,#";

        SetupJavascript();
        
        // Create a component with multiple prefixes
        var systemUnderTest = Render<AntDesign.Mentions>(
            @<AntDesign.Mentions Prefix="@prefixes">
                <AntDesign.MentionsOption Value="user1">User 1</AntDesign.MentionsOption>
                <AntDesign.MentionsOption Value="user2">User 2</AntDesign.MentionsOption>
            </AntDesign.Mentions>
        );

        var textArea = systemUnderTest.Find("textarea");

        // Test @ prefix
        JSInterop.Setup<Double[]>("AntDesign.interop.mentionsHelper.setPopShowFlag", _ => true);
        textArea.Input("@");
        JSInterop.VerifyInvoke("AntDesign.interop.mentionsHelper.setPopShowFlag", 1);

        // Test # prefix  
        JSInterop.Setup<Double[]>("AntDesign.interop.mentionsHelper.setPopShowFlag", _ => true);
        textArea.Input("#");
        JSInterop.VerifyInvoke("AntDesign.interop.mentionsHelper.setPopShowFlag", 2);
    }

    [Fact]
    public async Task ItShouldDisplayDifferentOptionsForDifferentPrefixes()
    {
        const string prefixes = "@,#";

        SetupJavascript();
        JSInterop.Setup<int>(
            "AntDesign.interop.mentionsHelper.getProp", 
            invocation => (string?)invocation.Arguments[1] == "selectionStart")
            .SetResult(1);
        JSInterop.Setup<OverlayPosition>("AntDesign.interop.overlayHelper.addOverlayToContainer", _ => true)
            .SetResult(new OverlayPosition());

        // Create a component with LoadOptions that returns different data based on prefix
        var systemUnderTest = Render<AntDesign.Mentions>(
            @<AntDesign.Mentions Prefix="@prefixes" LoadOptions="@LoadOptionsForMultiPrefix" />
        );

        var textArea = systemUnderTest.Find("textarea");

        // Test @ prefix - should show user options
        _testPrefixContext = "@";
        await textArea.InputAsync(new ChangeEventArgs { Value = "@" });
        
        var userOptions = systemUnderTest.FindAll(".ant-mentions-dropdown-menu-item");
        userOptions.Count.Should().Be(2, "@ prefix should show 2 user options");
        userOptions[0].TextContent.Trim().Should().Be("Alice");
        userOptions[1].TextContent.Trim().Should().Be("Bob");

        // Clear and test # prefix - should show different tag options
        _testPrefixContext = "#";
        await textArea.InputAsync(new ChangeEventArgs { Value = "#" });
        
        var tagOptions = systemUnderTest.FindAll(".ant-mentions-dropdown-menu-item");
        tagOptions.Count.Should().Be(3, "# prefix should show 3 tag options");
        tagOptions[0].TextContent.Trim().Should().Be("bug");
        tagOptions[1].TextContent.Trim().Should().Be("feature");
        tagOptions[2].TextContent.Trim().Should().Be("enhancement");
    }

    private string _testPrefixContext = "@";

    private Task<IEnumerable<MentionsDynamicOption>> LoadOptionsForMultiPrefix(string searchValue, CancellationToken cancellationToken)
    {
        // Return different options based on the current prefix context
        if (_testPrefixContext == "@")
        {
            // User options for @ prefix
            return Task.FromResult(new[]
            {
                new MentionsDynamicOption { Value = "alice", Display = @<text>Alice</text> },
                new MentionsDynamicOption { Value = "bob", Display = @<text>Bob</text> }
            }.AsEnumerable());
        }
        else // # prefix
        {
            // Tag options for # prefix
            return Task.FromResult(new[]
            {
                new MentionsDynamicOption { Value = "bug", Display = @<text>bug</text> },
                new MentionsDynamicOption { Value = "feature", Display = @<text>feature</text> },
                new MentionsDynamicOption { Value = "enhancement", Display = @<text>enhancement</text> }
            }.AsEnumerable());
        }
    }

#if NET6_0_OR_GREATER
    private IRenderedComponent<AntDesign.Mentions> GetTemplatedRenderSystemUnderTest()
    {
        JSInterop.Setup<TextAreaInfo>("AntDesign.interop.inputHelper.getTextAreaInfo", _ => true);

        return Render<AntDesign.Mentions>(
            @<AntDesign.Mentions>
                <ChildContent>
                    <AntDesign.MentionsOption Value="testUsername">Test User</AntDesign.MentionsOption>
                </ChildContent>
                <TextareaTemplate>
                    <TextArea RefBack=@context.RefBack
                          OnInput=@context.OnInput
                          BindOnInput=false
                          OnKeyDown=@context.OnKeyDown
                          Value=@context.Value />
                </TextareaTemplate>
            </AntDesign.Mentions>
        );
    }

    [Fact]
    public void ItShouldRenderWithGivenTemplate()
    {
        SetupJavascript();
        var systemUnderTest = GetTemplatedRenderSystemUnderTest();

        systemUnderTest.MarkupMatches(@"
            <div class=""ant-mentions"">
               <textarea class=""ant-input"" value="""" id:ignore style="""" rows=""2"" ></textarea>
            </div>
            <div class=""ant-mentions-dropdown ant-mentions-dropdown-placement-bottomLeft ant-mentions-dropdown-hidden"" 
                style=""position: absolute; display: none;"">
                <ul class=""ant-mentions-dropdown-menu ant-mentions-dropdown-menu-root ant-mentions-dropdown-menu-vertical"">
                    <li class=""ant-mentions-dropdown-menu-item"">
                        Test User
                    </li>
                </ul>
            </div>");
    }
#endif
}